﻿//===========================[ 7 / 6 / 2011 LVI ]=============================
//				Copyright c Vitaly Lyaschenko <SkyChameleon@gmail.com>
//								Source file
// Purpose: 
//=============================================================================
#include "cSoundSource.h"
#include "cFileManager.h"
#include "cwLogger.h"

#include "alCom.h"
#include "OggSoundData.h"


cSoundSource::cSoundSource() :
    m_Position(vec3_zero),
    m_Velosity(vec3_zero),
    m_isStreamed(false),
    m_isLooped(false),
    m_SourceID(0),
    m_Rate(0), m_Format(0),
    m_Pitch(1.0f), m_Gain(1.0f),
    pSoundData(nullptr)
{
    memset( m_BufID, 0, sizeof(uint)*2 );
}

cSoundSource::~cSoundSource()
{
    if( pSoundData )
        SAFE_DELETE( pSoundData );

    alDeleteSources( 1, &m_SourceID );

    if ( m_isStreamed )
        alDeleteBuffers( 2, m_BufID );
    else
        alDeleteBuffers( 1, m_BufID );
}

bool cSoundSource::createSound( const std::string &nameSound, const bool isLoop )
{
    pSoundData = new cOggSoundData( nameSound );

    m_isLooped = isLoop;
    m_isStreamed = true;

    if ( pSoundData->getNumChannels() == 2 )		// stereo
    {
        if ( pSoundData->getFormat() == 16 )
            m_Format = AL_FORMAT_STEREO16;
        else
            m_Format = AL_FORMAT_STEREO8;
    }
    else
    {
        if ( pSoundData->getFormat() == 16 )
            m_Format = AL_FORMAT_MONO16;
        else
            m_Format = AL_FORMAT_MONO8;
    }

    if ( pSoundData->getSize() < SOUNDBUF_SIZE * 2 )
        m_isStreamed = false;

    if ( m_isStreamed )
    {
        pBuff = new _8ubit[ SOUNDBUF_SIZE * 2 ];
        m_CurrentBuffer = 0;

        alGenSources( 1, &m_SourceID );
        alGenBuffers( 2, m_BufID );
    } else 
    {
        pBuff = new _8ubit[ pSoundData->getSize() ];

        int size = pSoundData->readToBuffer( pBuff );

        alGenBuffers( 1, m_BufID );
        alBufferData( m_BufID[0], m_Format, pBuff, size, pSoundData->getRate() );
        alGenSources( 1, &m_SourceID );

        alSourcei( m_SourceID, AL_BUFFER, m_BufID[0] );
        // AL_LOOPING – зациклен ли звук
        alSourcei( m_SourceID, AL_LOOPING, m_isLooped ? AL_TRUE : AL_FALSE );

        SAFE_DELETE( pBuff );
    }
    // AL_PITCH – тон звука
    alSourcef ( m_SourceID, AL_PITCH, m_Pitch );	
    // AL_GAIN – усиление звука
    alSourcef ( m_SourceID, AL_GAIN, m_Gain );
    // AL_POSITION – позиция источника в трёхмерных координатах
    alSourcefv( m_SourceID, AL_POSITION, m_Position.v_arr );
    // AL_VELOCITY – скорость движения звука
    alSourcefv( m_SourceID, AL_VELOCITY, m_Velosity.v_arr );

    return true;
}
///////////////////////////////////////////////////////////////////////////
void cSoundSource::sound_Play()
{
    ALint state = 0;
    int   size = 0;

    // проверка статуса воспроизводится ли уже звук
    alGetSourcei( m_SourceID, AL_SOURCE_STATE, &state );

    if ( state == AL_PLAYING )
        return;

    // если звук не на паузе и потоковое воспроизведение
    if ( state != AL_PAUSED && m_isStreamed )
    {
        size = pSoundData->readToBuffer( pBuff, SOUNDBUF_SIZE );

        alBufferData( m_BufID[0], m_Format, pBuff, size, pSoundData->getRate() );

        size = pSoundData->readToBuffer( pBuff, SOUNDBUF_SIZE );

        alBufferData( m_BufID[1], m_Format, pBuff, size, pSoundData->getRate() );
        alSourceQueueBuffers( m_SourceID, 2, m_BufID );
    }

    alSourcePlay( m_SourceID );
}
///////////////////////////////////////////////////////////////////////////
void cSoundSource::sound_Pause()
{
    alSourcePause( m_SourceID );
}
///////////////////////////////////////////////////////////////////////////
void cSoundSource::sound_Stop()
{
    alSourceStop( m_SourceID );

    pSoundData->seekTimeTo( 0 );

    if ( m_isStreamed )
    {
        ALint queued;

        alGetSourcei( m_SourceID, AL_BUFFERS_QUEUED, &queued );

        if ( queued > 0 )
            alSourceUnqueueBuffers( m_SourceID, 2, m_BufID );

        m_CurrentBuffer = 0;
    }
}
///////////////////////////////////////////////////////////////////////////
void cSoundSource::sound_Rewind()
{
     alSourceRewind( m_SourceID );
}

void cSoundSource::sound_Update()
{
    if ( m_isStreamed )
    {
        ALint processed;

        alGetSourcei( m_SourceID, AL_BUFFERS_PROCESSED, &processed );

        if ( processed == 0 )
            return;

        if ( processed == 1 )
        {
            alSourceUnqueueBuffers( m_SourceID, 1, &m_BufID[m_CurrentBuffer] );

            int size = pSoundData->readToBuffer( pBuff, SOUNDBUF_SIZE );

            if ( size > 0 || (size == 0 && m_isLooped) )
            {
                alBufferData( m_BufID[m_CurrentBuffer], m_Format, pBuff, size,
                    pSoundData->getRate() );

                alSourceQueueBuffers( m_SourceID, 1, &m_BufID[m_CurrentBuffer] );

                if ( size < SOUNDBUF_SIZE && m_isLooped )
                    pSoundData->seekTimeTo( 0 );
            }
            else
            {
                int queued;

                alGetSourcei( m_SourceID, AL_BUFFERS_QUEUED, &queued );

                if ( queued == 0 )
                    pSoundData->seekTimeTo( 0 );
            }

            m_CurrentBuffer = 1 - m_CurrentBuffer;
        }
        else
        {
            if ( processed == 2 )
            {
                alSourceUnqueueBuffers( m_SourceID, 2, m_BufID );

                m_CurrentBuffer = 0;

                sound_Play();
            }
        }
    }
}

// останавливает проигрывание, и удаляет источник по заданному идентификатору
void cSoundSource::releaseSound()
{
    alSourceStop( m_SourceID );

    if ( alIsSource( m_SourceID ) )
        alDeleteSources( 1, &m_SourceID );
}
///////////////////////////////////////////////////////////////////////////
// передвинуть источник звука
void cSoundSource::moveSound( const float x, const float y, const float z)
{
    float Pos[] = { x, y, z };
    alSourcefv( m_SourceID, AL_POSITION, Pos );
}
void cSoundSource::moveSound( const vec3 pos )
{
    moveSound( pos.x, pos.y, pos.z );
}
///////////////////////////////////////////////////////////////////////////
// задать позицию
void cSoundSource::setPosition( const float x, const float y, const float z )
{
    m_Position.x = x;
    m_Position.y = y;
    m_Position.z = z;
}
///////////////////////////////////////////////////////////////////////////
// задать скорость
void cSoundSource::setVelosity( const float x, const float y, const float z )
{
    m_Velosity.x = x;
    m_Velosity.y = y;
    m_Velosity.z = z;
}
///////////////////////////////////////////////////////////////////////////
void cSoundSource::setPitch( float pitch )
{
    m_Pitch = pitch;

    alSourcef ( m_SourceID, AL_PITCH, m_Pitch );
}
///////////////////////////////////////////////////////////////////////////
void cSoundSource::setGain( float gain )
{
    m_Gain = gain;

    alSourcef ( m_SourceID, AL_GAIN, gain );
}

void cSoundSource::setLooping( bool flag )
{
    m_isLooped = flag;

    alSourcei ( m_SourceID, AL_LOOPING, m_isLooped ? AL_TRUE : AL_FALSE );
}